package com.zx._01_基础._010_util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.CompletableFuture;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * zip解压缩
 */
public class ZipDemo {

    public static final String FILE_NAME = "1.gif";
    public static final String FILE_PATH = "D://1.gif";
    public static final String TARGET_PATH = "D://1.zip";

    public static void main(String[] args) throws FileNotFoundException {
        //zip1();// 耗时 115658
        //zip2();// 耗时      1435
        //zip3();// 耗时      2153
        //zip4();// 耗时           1279
        zip5();// 耗时           1279
    }

    public static void zip1() throws FileNotFoundException {
        File file = new File(TARGET_PATH);
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) {
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                try (InputStream input = new FileInputStream(FILE_PATH)) {
                    zipOutputStream.putNextEntry(new ZipEntry(i + FILE_NAME));
                    // 方式1
                    int temp = 0;
                    //read 方法 这是一个调用本地方法与原生操作系统进行交互，从磁盘中读取数据。每读取一个字节的数据就调用一次本地方法与操作系统交互，是非常耗时的
                    while ((temp = input.read()) != -1) {
                        zipOutputStream.write(temp);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void zip2() throws FileNotFoundException {
        File file = new File(TARGET_PATH);
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) {
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                try (InputStream input = new FileInputStream(FILE_PATH)) {
                    zipOutputStream.putNextEntry(new ZipEntry(i + FILE_NAME));
                    // 方式2, 用户自己开辟缓冲空间
                    int len = 0;
                    byte[] b = new byte[1024 * 5];
                    while ((len = input.read(b)) != -1) {
                        zipOutputStream.write(b, 0, len);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void zip3() throws FileNotFoundException {
        File file = new File(TARGET_PATH);
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(file));
                BufferedOutputStream bOutputStream = new BufferedOutputStream(zipOutputStream)
        ) {
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                try (BufferedInputStream bInputStream = new BufferedInputStream(new FileInputStream(FILE_PATH));) {
                    zipOutputStream.putNextEntry(new ZipEntry(i + FILE_NAME));
                    // 方式3， 使用JDK工具的缓冲空间
                    int temp = 0;
                    //BufferedInputStream内部封装了一个byte数组用于存放数据，默认大小是8192
                    while ((temp = bInputStream.read()) != -1) {
                        bOutputStream.write(temp);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void zip4() throws FileNotFoundException {
        File file = new File(TARGET_PATH);
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(file));
                WritableByteChannel writableByteChannel = Channels.newChannel(zipOutputStream);
        ) {
            long begin = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                try (
                        FileInputStream inputStream = new FileInputStream(FILE_PATH);
                        FileChannel channel = inputStream.getChannel();
                ) {
                    zipOutputStream.putNextEntry(new ZipEntry(i + FILE_NAME));
                    // 我们可以看到这里并没有使用ByteBuffer进行数据传输，而是使用了transferTo的方法。这个方法是将两个通道进行直连
                    // 通过方法上面的注释，可以看出来使用transferTo的效率比循环一个Channel读取出来然后再循环写入另一个Channel好
                    // 操作系统能够直接传输字节从文件系统缓存到目标的Channel中，而不需要实际的copy阶段。
                    channel.transferTo(0, inputStream.available(), writableByteChannel);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void zip5() throws FileNotFoundException {
        File file = new File(TARGET_PATH);
        File image = new File(FILE_PATH);
        System.out.println(image.length());
        try (
                WritableByteChannel writableByteChannel = Channels.newChannel(new FileOutputStream(file));
        ) {
            long begin = System.currentTimeMillis();
            Pipe pipe = Pipe.open();
            //异步任务
            CompletableFuture.runAsync(() -> {
                try (ZipOutputStream zos = new ZipOutputStream(Channels.newOutputStream(pipe.sink()));
                        WritableByteChannel out = Channels.newChannel(zos)) {
                    for (int i = 0; i < 10; i++) {
                        zos.putNextEntry(new ZipEntry(i + FILE_NAME));
                        FileInputStream inputStream = new FileInputStream(image);
                        FileChannel jpgChannel = inputStream.getChannel();
                        jpgChannel.transferTo(0, inputStream.available(), out);
                        jpgChannel.close();
                        inputStream.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            //获取读通道
            ReadableByteChannel readableByteChannel = pipe.source();
            ByteBuffer buffer = ByteBuffer.allocate(((int) image.length()) * 10);
            while (readableByteChannel.read(buffer) >= 0) {
                buffer.flip();
                writableByteChannel.write(buffer);
                buffer.clear();
            }

            long end = System.currentTimeMillis();
            System.out.println(end - begin);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
